﻿using Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using S7.Net;
using SqlSugar.IOC;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using ZR.Model.Models.ProductionControl;
using CKS.PLC.Core.Event;
using CKS.Core.Framework;
using ZR.Infrastructure;
using Org.BouncyCastle.Tls;

namespace CKS.PLC.Core.Siemens {

    public abstract class SiemensClientBase<T> : ISiemensClient {

        #region Fields

        private readonly object _lockCache = new object();

        private readonly IServiceProvider _serviceProvider;

        private readonly ILogger<T> _logger;

        private readonly IFreeSql<MesDB> _freeSqlClient = null;

        /// <summary>
        /// 所有点位
        /// </summary>
        private List<CksPlcDataPosition> AllDataPosition = null;

        /// <summary>
        /// 订阅点位
        /// </summary>
        private List<CksPlcDataPosition> SubscriptionPosition = null;

        /// <summary>
        /// LastHeartTime
        /// </summary>
        private DateTime LastHeartTime = DateTime.Now;

        /// <summary>
        /// BlockDb 数据块 
        /// BlockLenth 大小
        /// Task<byte[]> 异步读取的结果
        /// </summary>
        private List<DataBlock> DataBlocks = new List<DataBlock>();   // 需要读取的数据块

        private Dictionary<int, List<DecodeValue>> CacheValues = new Dictionary<int, List<DecodeValue>>();

        #endregion


        public SiemensClientBase(PlcConfig config, IServiceProvider serviceProvider) {
            _serviceProvider = serviceProvider;
            _logger = _serviceProvider.GetService<ILogger<T>>();
            _freeSqlClient = _serviceProvider.GetService<IFreeSql<MesDB>>();
            this.PlcInfo = config;
            //try
            //{
            //    // db = DbTransient.Sugar;
            //    //logger.LogInformation($"{_config.WorkstationCode} MySql: {db.ContextID}");
            //}
            //catch (Exception)
            //{
            //}
        }

        #region Props

        public T PlcSession { get; set; }

        public PlcConfig PlcInfo { get; set; }

        public int HeartValue { get; set; } = 0;

        public bool IsConnected { get; set; } = false;

        /// <summary>
        /// 当前PLC是否在线
        /// </summary>
        public bool IsOnline { get; set; }

        #endregion

        #region Abstract

        public abstract T GetSdkInstance();

        public abstract Task ConnectPlc(CancellationToken cancellation);

        public abstract void WriteInt16(int db, int startByteAdr, Int16 value);

        public abstract short ReadInt16(int db, int startByteAdr);

        public abstract Task<byte[]> ReadBytesAsync(int db, int startByteAdr, int count);

        public abstract void SetValue(CksPlcDataPosition s7Point, string configValue);

        public abstract object GetValue(CksPlcDataPosition s7Point);

        #endregion

        #region Methods

        /// <summary>
        /// 启动客户端
        /// </summary>
        /// <param name="cancellation"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task StartAsync(CancellationToken cancellation) {
            await Task.Yield();

            GetSdkInstance();

            BindCacheData();

            _ = Execute(cancellation);

            await Task.CompletedTask;
        }

        /// <summary>
        /// 每x毫秒读取一次
        /// </summary>
        /// <param name="cancellation"></param>
        /// <returns></returns>
        public async Task Execute(CancellationToken cancellation) {

            try {
                while (!cancellation.IsCancellationRequested) {
                    int STEP = 1;
                    try {
                        if (CheckUpdateCache() > 0) {
                            await Task.Delay(1000);
                            _logger.LogError($"检查并更新缓存，返回非0");
                            continue;
                        }

                        STEP = 2;
                        await ConnectPlc(cancellation);

                        STEP = 3;
                        _ = DoHeart();

                        STEP = 4;
                        await ExecuteMain();
                    } catch (Exception ex) {
                        EventBus.AddLogErrorToQueues($"{PlcInfo.WorkstationCode} STEP:{STEP} 异常{ex.GetType()}: {ex.Message}！");
                    }

                    await Task.Delay(30, cancellation);
                }

                await Task.CompletedTask;

            } catch (Exception ex) {
                _logger.LogWarning($"异常 {ex.Message}！");
            }
        }

        /// <summary>
        /// 读取订阅点位
        /// </summary>
        /// <returns></returns>
        public async Task ExecuteMain() {
            try {

                if (!IsConnected) { return; }

                if (DataBlocks.Count == 0) { return; }

                //获取需要读取的所有数据块的数据
                List<Task<byte[]>> tasks = new List<Task<byte[]>>();

                for (int i = 0; i < DataBlocks.Count; i++) {
                    var block = DataBlocks[i];

                    var task = ReadBytesAsync(block.BlockDb, 0, block.BlockLength);

                    block.ReadTask = task;

                    tasks.Add(task);
                }

                await Task.WhenAll(tasks);

                foreach (var block in DataBlocks) {
                    //异步任务结果
                    var blockBytes = block.ReadTask.Result; //异步任务结果
                    //解析所有点位数据
                    var DecodeValues = block.Decoder.Decode(blockBytes);
                    //缓存
                    SetCacheValues(block.BlockDb, DecodeValues);
                    //获取需要监听的点位
                    var subscriptionCodes = DecodeValues.FindAll(a => a.Position.IsListen);
                    foreach (var val in subscriptionCodes) {
                        val.AllValues = DecodeValues;
                        //如果值发生变化或者是实时采集数据就执行订阅方法
                        if (val.IsChange || val.Position.EffectType == ZR.Model.Enums.eEffectType.RealTime_Collection || val.Position.EffectType == ZR.Model.Enums.eEffectType.RealTime_Subscription) {
                            val.SiemensClient = this;

                            val.AllPositions = AllDataPosition;

                            val.LinkPositions = AllDataPosition.FindAll(a => a.LinkActionId == val.Position.ActionId);

                            ExecuteSubscriptionAction(val);

                            val.OldValue = val.Value; //处理完成后将值赋给旧值变量
                        }
                    }

                }
            } catch (Exception ex) {
                _logger.LogError($"读取订阅点位数据异常，{ex.Message}{ex.StackTrace}！");
                EventBus.AddLogErrorToQueues($"读取订阅点位据异常，{PlcInfo.PlcAddress}-{PlcInfo.WorkstationCode}，{ex.Message ?? ex.InnerException.Message}");
            }

        }

        /// <summary>
        /// 执行订阅方法
        /// </summary>
        public void ExecuteSubscriptionAction(DecodeValue value) {
            // 获取 ActionBase 类型
            var ActionBaseType = PlcActionDLLManage.GetActionBaseType();

            // 获取 GetInstance 方法
            MethodInfo method = ActionBaseType.GetMethod("GetInstance", new Type[] { typeof(DecodeValue) });

            // 检查方法是否找到
            if (method == null) {
                EventBus.AddLogErrorToQueues($"执行订阅方法异常，{PlcInfo.PlcAddress}-{PlcInfo.WorkstationCode}，Postion：{value.Position.BytePosition}，Method GetInstance not found.");
                return;
            }

            // 使用反射调用方法，传递 value 参数
            var instance = method.Invoke(null, [value]) as BasePlcAction;

            // 检查返回结果是否正确
            if (instance == null) {
                EventBus.AddLogErrorToQueues($"执行订阅方法异常，{PlcInfo.PlcAddress}-{PlcInfo.WorkstationCode}，Postion：{value.Position.BytePosition}，Failed to cast the result to BasePlcAction.");
                return;
            }

            Dictionary<string, object> pairs = new Dictionary<string, object>();
            pairs.Add(BasePlcAction._execBatchCodeKey, $"{Guid.NewGuid().ToString("N")}");

            bool res = instance.ExecuteBefor(pairs);
            if (!res) { return; }
            instance.Execute(pairs);
            instance.ExecuteAfter(pairs);

        }

        // 声明一个类级别的布尔变量
        private bool heartState = false;
        private int plcLastHeart = -1;
        private bool lastConnected = false;
        private DateTime LastPlcHeart = App.MinTime;

        /// <summary>
        /// 写入Mes心跳
        /// </summary>
        public async Task DoHeart() {
            try {
                var ts = DateTime.Now - LastPlcHeart;
                if (ts.TotalSeconds < 5 && IsConnected) {
                    IsOnline = true;
                    App.AddOnlinePlcs(PlcInfo.Id);
                } else {
                    IsOnline = false;
                    App.RemoveOnlinePlcs(PlcInfo.Id);
                }

                ts = DateTime.Now - LastHeartTime;
                if (ts.TotalMilliseconds > 500) {

                    // 在DoHeart方法中
                    heartState = !heartState; // 每次取反，实现0和1的交替
                    Int16 valueToWrite = (short)(heartState ? 1 : 0);

                    //查询心跳点位
                    var points = AllDataPosition.FindAll(a => a.EffectType == ZR.Model.Enums.eEffectType.MES_Heartbeat);
                    if (points != null && IsConnected) {
                        foreach (var point in points) {
                            SetValue(point, valueToWrite.ToString());
                            // WriteInt16(point.BlockDb, (int)point.BytePosition, valueToWrite);
                        }

                        LastHeartTime = DateTime.Now;

                        //读取PLC心跳值
                        var plcheart = AllDataPosition.Find(a => a.EffectType == ZR.Model.Enums.eEffectType.PLC_Heartbeat);
                        if (plcheart != null) {
                            switch (plcheart.ParseType.ToLower()) {
                                case "bool":
                                    int bitindex = (int)(plcheart.BytePosition * 10) % 10;
                                    var boolByte = await ReadBytesAsync(plcheart.BlockDb, (int)plcheart.BytePosition, 1);
                                    HeartValue = DataConversion.GetBit(boolByte[0], bitindex) ? 1 : 0;
                                    break;
                                default:
                                    HeartValue = ReadInt16(plcheart.BlockDb, (int)plcheart.BytePosition);
                                    break;
                            }
                        }
                    }

                    if (plcLastHeart != HeartValue || IsConnected != lastConnected) {
                        //logger.Information($"变更心跳值{plcHeartValue}");
                        // EventBus.PlcHeart(this, plcHeartValue);
                        LastPlcHeart = DateTime.Now;
                        if (IsConnected == false)
                            HeartValue = 0;
                        EventBus.PlcHeart(this, HeartValue);
                    }

                    lastConnected = IsConnected;

                } else {
                    _logger.LogError($"写入MES心跳失败");
                }
            } catch (Exception ex) {
                _logger.LogError($"写入MES心跳异常 {ex.Message}");
            }
        }

        /// <summary>
        /// 写入缓存值
        /// </summary>
        /// <param name="db"></param>
        /// <param name="values"></param>
        public void SetCacheValues(int db, List<DecodeValue> values) {
            lock (_lockCache) {
                CacheValues[db] = values;
            }
        }

        /// <summary>
        /// 获取缓存值
        /// </summary>
        /// <param name="db"></param>
        /// <returns></returns>
        public List<DecodeValue> GetCacheValues(int db) {
            lock (_lockCache) {
                try {
                    if (!IsConnected)
                        return null;
                    if (!CacheValues.ContainsKey(db))
                        return null;
                    return CacheValues[db];
                } catch (Exception) {
                    return null;
                }
            }
        }

        #endregion

        #region Private

        private void BindCacheData() {
            //获取需要读取的数据块
            AllDataPosition = _freeSqlClient.Select<CksPlcDataPosition>().Where(a => a.PlcId == PlcInfo.Id && a.Module == 1).ToList();
            SubscriptionPosition = AllDataPosition.FindAll(a => a.IsListen || a.EffectType == ZR.Model.Enums.eEffectType.PLC_Heartbeat);
            //提取订阅点的数据块批量读取
            DataBlocks.Clear();
            foreach (var item in SubscriptionPosition) {
                var findex = DataBlocks.FindIndex(a => a.BlockDb == item.BlockDb);
                if (findex < 0) {
                    //该DB块内的所有数据
                    var points = AllDataPosition.FindAll(a => a.BlockDb == item.BlockDb);
                    DataBlocks.Add(new(item.BlockDb, item.BlockLength, null, new SieminDecoder(points)));
                }
            }
        }

        /// <summary>
        /// 检查并更新缓存 返回0 OK 其他值代表没有数据
        /// </summary>
        private int CheckUpdateCache() {
            int result = 0;
            //检查当前PLC状态是否禁用 
            //检查当前PLC的通信点位是否有变化，有变化需要重新加载
            var model = _freeSqlClient.Select<PlcConfig>().Where(a => a.Id == PlcInfo.Id).First();
            if (model == null || model.IsEnabled == false) {
                result = 1;
            }
            PlcInfo.PlcAddress = model.PlcAddress;
            bool pointIsChange = false;
            var alls = _freeSqlClient.Select<CksPlcDataPosition>().Where(a => a.PlcId == PlcInfo.Id && a.Module == 1).ToList();
            if (alls.Count != AllDataPosition.Count)
                pointIsChange = true;
            else if (alls.Count > 0) {
                for (int i = 0; i < alls.Count; i++) {
                    var id = alls[i].Id;
                    var a = alls[i];
                    var b = AllDataPosition.Find(x => x.Id == id);
                    if (b == null)
                        pointIsChange = true;
                    else if (b.BytePosition != a.BytePosition)
                        pointIsChange = true;
                    else if (b.BlockDb != a.BlockDb)
                        pointIsChange = true;
                    else if (!b.ParseType.Equals(a.ParseType))
                        pointIsChange = true;
                }
            } else {//没有点位数据
                result = 1;
            }
            if (pointIsChange) {
                BindCacheData();
            }
            return result;
        }

        #endregion
    }


    public class DataBlock {
        public DataBlock(int blockDb, int blockLenth, Task<byte[]> readTask, SieminDecoder decoder) {
            BlockDb = blockDb;
            BlockLength = blockLenth;
            ReadTask = readTask;
            Decoder = decoder;
        }

        public int BlockDb { get; set; }

        public int BlockLength { get; set; }

        public Task<byte[]> ReadTask { get; set; }

        public SieminDecoder Decoder { get; set; }
    }

}
